(* Creme Caramel Compiler (Triple C) - Michele Caceffo - 2008 *)

open Syntaxtree;;
open Exceptions;;
open Intermediate;;

(** Here are all the functions used to generate the declaration table *)

(** Used to create a single variable entry on the dec_table
    @param idx The index of the variable (1 for single variables, the vector index for Vector or Matrix)
    @param dim The dimension of a line (1 for single variable, the number of columns for Vector or Matrix)
    @param bt The Basic type of the variable (int or float)
    @param ic The instance of the IntermediateCode class
    @param b True if the variable is declared into a subroutine, false if it's created globally
    @return The entry which is binded into the Hashtable
*)
let rec genBtype (idx: int) (dim: int) (bt: bType) (ic: intermediateCode) (b: bool) = match bt with
        Int     ->  let off = (ic#stack) in (idx, dim, (Off(off, DInt, b)))
      | Float   ->  let off = (ic#stack) in (idx, dim, (Off(off, DFloat, b)))

(** Used to build the correct number of entries into the Declaration Hashtable 
    @param id The ide of the variable, which is used as key for the table
    @param t The generic Type of the variable
    @param table The declaration Table
    @param ic The instance of the IntermediateCode class
    @param b True if the variable is declared into a subroutine, false if it's created globally
*)
let genGtype (id: ide) (t: gType) (table: dec_table) (ic: intermediateCode) (b: bool): unit = match t with
        Basic(bt)       ->  Hashtbl.add table id (genBtype 1 1 bt ic b)
      | Vector(x,bt)    ->  ( 
                             (* Nella dichiarazione scrivo nella tabella solo la testa della mia lista,dopodiche inizializzo l'array
                                settando ogni puntatore al successivo implementando cosi una lista concatenata di base*)
                            let (idx,dim,stackoff) = (genBtype 0 x bt ic b) in
                            Hashtbl.add table id (idx,dim,stackoff);           
                            let rec initializeArray index stof =(
                                if (index!=x) then (let (_,_,stof2)= (genBtype 0 0 bt ic b) in
                                                    ic#addInstr (SAP,stof ,stof2,Null );
                                                    initializeArray (index+1) stof2)
                                              else ( ic#addInstr (SAP,stof ,Val(I(-1)),Null );)
                            )in initializeArray 0 stackoff
                            )
      | Matrix(x,y,bt)  ->  (
                             let lista = (
                                          let rec aux n = match n with
                                            0 -> [genBtype 0 y bt ic b]
                                          | m -> (genBtype m y bt ic b)::(aux (m-1))
                                          in (aux (x*y-1))
                                          )
                             in
                             for index=0 to (x*y-1) do (Hashtbl.add table id (List.nth lista index)) done)
                            (** This works only with bi-dimensional Matrix, not with n-dimensional Vectors *)

(** Generate the table of declarations from the dec list
    @param decl The declaration list
    @param table The declaration Hashtable
    @param ic The instance of the IntermediateCode class
    @param b True if the variable is declared into a subroutine, false if it's created globally
    @return The updated table of declaration
*)
let rec genDecTable (decl: dec list) (table: dec_table) (ic: intermediateCode) (b: bool) = match decl with
        []              ->  table
      | (Dec(id,t))::xs ->  (genGtype id t table ic b); genDecTable xs table ic b

(** Find the binded value corresponding to the ide into the Hashtable (where ide is the identifier for a variable, NOT Vectors or Matrix)
    @param Ide(id) The ide to search into the table
    @param table The declaration table
    @return The value binded with Ide(id) into table
    @raise NON_DECLARED_VARIABLE When the requested ide isn't binded into the table
*)
let getVarDeclaration (Ide(id): ide) (table: dec_table) = 
        try (
             Hashtbl.find table (Ide(id))
        ) with Not_found -> raise (NON_DECLARED_VARIABLE ("ERROR: Undeclared variable: "^ id ^ "\n"))

(** Find the binded value corresponding to the ide into the Hashtable (where ide is the identifier for Vectors or Matrix)
    @param Ide(id) The ide to search into the table
    @param x The X coordinate of the Vector/Matrix
    @param y The Y coordinate of the Matrix (-1 if the ide refers to a Vector, which has only one component)
    @param table The declaration table
    @return The value binded with Ide(id) with the specified components into table
    @raise NON_DECLARED_VARIABLE When the requested ide isn't binded into the table
*)
let getVecDeclaration (Ide(id): ide) (x: int) (y: int) (table: dec_table) = 
        try (
             let elList = Hashtbl.find_all table (Ide(id)) in
             if (y = -1) then
                List.nth elList x
             else (
                let (_, dim, _) = List.nth elList 0 in 
                    List.nth elList ((x*dim) + y)
             )
        ) with Not_found -> raise (NON_DECLARED_VARIABLE ("ERROR: Undeclared variable: "^ id ^ "\n"))

(** Check if the variable is binded into the Hashtable
    @param id The ide of the variable to check
    @param table The declaration Table
    @return True of the variable is binded, False otherwise
*)
let isVarDeclared (id: ide) (table: dec_table): bool = Hashtbl.mem table id
